import 'dart:async';
import 'dart:io';
import 'dart:isolate';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class FlutterBugly {
  FlutterBugly._();

  static const MethodChannel _channel = const MethodChannel('flutter_bugly_plugin');

  static const bool inProduction = const bool.fromEnvironment("dart.vm.product");

  ///初始化
  ///请先调用 WidgetsFlutterBinding.ensureInitialized();
  ///noBugly 是否需要初始化bugly
  ///debugPost debug环境是否上报错误
  ///callback bugly初始化成功回调
  static Future init({String? androidAppId, String? iOSAppId, bool noBugly = true, bool debugPost = false, Function? callback}) async {
    if (!noBugly) {
      assert((Platform.isAndroid && androidAppId != null) || (Platform.isIOS && iOSAppId != null));
      Map<String, Object> map = {"appId": Platform.isAndroid ? androidAppId! : iOSAppId!};
      try {
        await _channel.invokeMethod('initBugly', map);
      } catch (e) {
        print(e);
      }
    }
    Isolate.current.addErrorListener(new RawReceivePort((dynamic pair) {
      var isolateError = pair as List<dynamic>;
      var _error = isolateError.first;
      var _stackTrace = isolateError.last;
      Zone.current.handleUncaughtError(_error, _stackTrace);
    }).sendPort);
    // This creates a [Zone] that contains the Flutter application and stablishes
    // an error handler that captures errors and reports them.
    //
    // Using a zone makes sure that as many errors as possible are captured,
    // including those thrown from [Timer]s, microtasks, I/O, and those forwarded
    // from the `FlutterError` handler.
    //
    // More about zones:
    //
    // - https://api.dartlang.org/stable/1.24.2/dart-async/Zone-class.html
    // - https://www.dartlang.org/articles/libraries/zones
    runZonedGuarded<Future<Null>>(() async {
      if (callback != null) {
        callback();
      }
    }, (error, stackTrace) {
      if (!inProduction) {
        FlutterError.dumpErrorToConsole(FlutterErrorDetails(exception: error, stack: stackTrace));
        if (debugPost) {
          postException(message: error.toString(), stackTrace: stackTrace.toString());
        }
      } else {
        postException(message: error.toString(), stackTrace: stackTrace.toString());
      }
    });
    // This captures errors reported by the Flutter framework.
    FlutterError.onError = (details) {
      Zone.current.handleUncaughtError(details.exception, details.stack ?? StackTrace.empty);
    };
  }

  ///上报自定义异常信息，data为文本附件
  ///Android 错误分析=>跟踪数据=>extraMessage.txt
  ///iOS 错误分析=>跟踪数据=>crash_attach.log
  static Future<Null> postException({@required String? message, @required String? stackTrace, Map? data}) async {
    var map = {};
    map.putIfAbsent("crashMessage", () => message);
    map.putIfAbsent("crashStack", () => stackTrace);
    if (data != null) map.putIfAbsent("crashData", () => data);
    try {
      await _channel.invokeMethod('postCrash', map);
    } catch (e) {
      print(e);
    }
  }
}
